/************************************************************************
*
*  LibTLib
*  Copyright (C) 2010  Thor Qin
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
*
* Author: Thor Qin
* Bug Report: thor.qin@gmail.com
*
**************************************************************************/

#include "syntax.h"

extern const unsigned char syn_bc[];
extern const unsigned int syn_bc_length;
extern const unsigned char syn_ubc[];
extern const unsigned int syn_ubc_length;
extern const unsigned char syn_wbc[];
extern const unsigned int syn_wbc_length;

namespace tlib
{
namespace syn
{

using namespace std;


#pragma pack(push)
#pragma pack(4)

typedef struct _lex_data
{
	unsigned int header_size;
	unsigned int data_size;
	unsigned int char_size;
	unsigned int char_map_offset;
	unsigned int char_map_count;
	unsigned int transit_map_offset;
	unsigned int transit_map_state_count;
	unsigned int transit_map_input_count;
	unsigned int action_info_offset;
	unsigned int action_info_count;
	unsigned int action_names_offset;
	unsigned int action_names_size;
} LexData;

typedef struct _syn_data
{
	unsigned int header_size;
	unsigned int data_size;
	unsigned int lexical_offset;
	unsigned int lexical_size;
	unsigned int syntax_offset;
	unsigned int syntax_size;
} SynData;

#pragma pack(pop)

template <typename T>
Syntax<T>::Syntax()
: best_match(false), _data(0), _allocated(false)
{
}

template <typename T>
Syntax<T>::~Syntax()
{
	if (_data && _allocated)
		free(_data);
}

template <typename T>
typename Syntax<T>::SyntaxPtr Syntax<T>::create(const std::basic_string<T>& syntax)
throw(std::runtime_error)
{
	std::basic_istringstream<T> in(syntax);
	return create_by_stream(in);
}

template <>
Syntax<char>::SyntaxPtr Syntax<char>::create_by_file(const std::string& filename)
throw(std::runtime_error)
{
	std::string str;
	load_text_file_to_string(filename, str);
	return create(str);
}

template <>
Syntax<unsigned char>::SyntaxPtr Syntax<unsigned char>::create_by_file(const std::string& filename)
throw(std::runtime_error)
{
	std::string str;
	load_text_file_to_string(filename, str);
	return create((const unsigned char*)str.c_str());
}

template <>
Syntax<wchar_t>::SyntaxPtr Syntax<wchar_t>::create_by_file(const std::string& filename)
throw(std::runtime_error)
{
	std::wstring str;
	load_text_file_to_wstring(filename, str);
	return create(str);
}


template <typename T>
bool valid_data(const SynData* data)
{
	if (!data)
		return false;
	if (data->header_size != sizeof(SynData))
		return false;
	if (data->data_size < data->header_size)
		return false;

	return true;
}

template <typename T>
typename Syntax<T>::SyntaxPtr Syntax<T>::create_by_bc(const void* data, unsigned long data_len)
throw(std::runtime_error)
{
	if (data_len < sizeof(SynData))
		throw runtime_error("Invalid data format.");

	SynData* syn_data = (SynData*)data;
	if (!valid_data<T>(syn_data))
		throw runtime_error("Invalid data format.");

	if (data_len < syn_data->data_size)
		throw runtime_error("Invalid data format.");

	SyntaxPtr syntax(new Syntax());
	syntax->_data = malloc(syn_data->data_size);
	if (!syntax->_data)
	{
		throw runtime_error("Out of memory.");
	}
	syntax->_allocated = true;
	memcpy(syntax->_data, data, syn_data->data_size);

	syntax->_lex = lex::Lexical<T>::create_by_static_bc(
			((char*)syntax->_data) + ((SynData*)syntax->_data)->lexical_offset,
			((SynData*)syntax->_data)->lexical_size);
	return syntax;
}

template <typename T>
typename Syntax<T>::SyntaxPtr Syntax<T>::create_by_static_bc(const void* data, unsigned long data_len)
throw(std::runtime_error)
{
	if (data_len < sizeof(SynData))
		throw runtime_error("Invalid data format.");

	SynData* syn_data = (SynData*)data;
	if (!valid_data<T>(syn_data))
		throw runtime_error("Invalid data format.");

	if (data_len < syn_data->data_size)
		throw runtime_error("Invalid data format.");

	SyntaxPtr syntax(new Syntax());
	syntax->_data = (void*)data;
	syntax->_allocated = false;
	syntax->_lex = lex::Lexical<T>::create_by_static_bc(
			((char*)syntax->_data) + ((SynData*)syntax->_data)->lexical_offset,
			((SynData*)syntax->_data)->lexical_size);
	return syntax;

}

template <typename T>
typename Syntax<T>::SyntaxPtr Syntax<T>::create_by_bc_file(const std::string& filename)
throw(std::runtime_error)
{
	ifstream infile(filename.c_str(), ios::in | ios::binary);
	if (!infile.is_open())
		throw runtime_error("Can't open file.");

	SynData data;
	infile.read((char*)&data, sizeof(SynData));
	if (infile.gcount() < (int)sizeof(SynData))
	{
		infile.close();
		throw runtime_error("Invalid file format.");
	}

	if (!valid_data<T>(&data))
	{
		infile.close();
		throw runtime_error("Invalid file format.");
	}

	SyntaxPtr syntax(new Syntax());
	syntax->_data = malloc(data.data_size);
	if (!syntax->_data)
	{
		throw runtime_error("Out of memory.");
	}
	syntax->_allocated = true;

	memcpy(syntax->_data, &data, data.header_size);
	infile.read((char*)syntax->_data + data.header_size, data.data_size - data.header_size);
	if (infile.gcount() < (int)(data.data_size - data.header_size))
	{
		infile.close();
		throw runtime_error("Invalid file format.");
	}

	syntax->_lex = lex::Lexical<T>::create_by_static_bc(
			((char*)syntax->_data) + ((SynData*)syntax->_data)->lexical_offset,
			((SynData*)syntax->_data)->lexical_size);

	return syntax;
}


template <typename T>
void Syntax<T>::save_bc(const std::string& filename) throw(std::runtime_error)
{
	if (!_data)
		throw std::runtime_error("Not initialized.");
	ofstream outfile(filename.c_str(), ios::out | ios::binary);
	if (!outfile.good())
		throw runtime_error("Can't open file.");

	SynData* dfa_data = (SynData*)_data;
	outfile.write((char*)_data, dfa_data->data_size);
	if (!outfile.good())
	{
		outfile.close();
		throw runtime_error("Write file error.");
	}
	outfile.close();
}

template <typename T> inline
void get_syn_bc(const unsigned char*& bc, unsigned int& len);
template <> inline
void get_syn_bc<char>(const unsigned char*& bc, unsigned int& len)
{
	bc = syn_bc;
	len = syn_bc_length;
}
template <> inline
void get_syn_bc<unsigned char>(const unsigned char*& bc, unsigned int& len)
{
	bc = syn_ubc;
	len = syn_ubc_length;
}
template <> inline
void get_syn_bc<wchar_t>(const unsigned char*& bc, unsigned int& len)
{
	bc = syn_wbc;
	len = syn_wbc_length;
}

#define SYN_ACTION_BASE (0x80000000)

template <typename T>
void parse_error(const std::string& msg, lex::Token<T>& token)
{
	std::stringstream iss;
	iss << "Parse error: " << msg << ", line"
			<< token.line << ", character " << token.line_pos << ".";
	throw std::runtime_error(iss.str());
}

template <typename T>
void parse_error(lex::Token<T>& token)
{
	std::stringstream iss;
	iss << "Parse error: line"
			<< token.line << ", character " << token.line_pos << ".";
	throw std::runtime_error(iss.str());
}

template <typename T>
void parse_error(const std::string& msg)
{
	std::stringstream iss;
	iss << "Parse error: " << msg << ".";
	throw std::runtime_error(iss.str());
}

template <typename T> inline
const T* separator();
template <> inline
const char* separator<char>()
{
	return ",";
}
template <> inline
const unsigned char* separator<unsigned char>()
{
	return (const unsigned char*)",";
}
template <> inline
const wchar_t* separator<wchar_t>()
{
	return L",";
}


// Action ID both of lexical token and syntax define
typedef std::vector<unsigned int> ActionList;

template <typename T>
class SynSchema
{
public:
	typedef std::vector<SynSchema<T> > Array;
	std::basic_string<T> name;
	ActionList actions;
	ActionList valued_actions;
};


template <typename T>
class SynDefine
{
public:
	typedef std::vector<SynDefine<T> > Array;
	unsigned int action;
	std::basic_string<T> name;
	typename SynSchema<T>::Array schemas;
};


//-- 现在传递给这个函数的是一个复杂的结构，
//-- SyntaxPtr 是外包装，其实关心的是里面的数据结构：SynData
//-- 也就是 SyntaxPtr 中的 data 指针指向的数据块，里面包含了词法分析器的数据，和语法分析器的数据段
//
template <typename T>
void build_syn_dfa(const typename SynDefine<T>::Array& defines,
		void*& syn_data, unsigned int& syn_data_size)
{

}

template <typename T>
typename Syntax<T>::SyntaxPtr Syntax<T>::create_by_stream(std::basic_istream<T>& in)
throw(std::runtime_error)
{
	typedef enum _syn_token_type
	{
		T_LEX_DEF = 1,
		T_SPACE,
		T_NAME,
		T_X_NAME,
		T_COLON,
		T_OR,
		T_SEMICOLON,
		T_SCHEMA
	} SynToken;
	typedef enum _syn_state
	{
		X_BEGIN = 0,
		X_XNAME,
		X_COLON,
		X_NAME,
		X_OR,
		X_SCHEMA
	} SynState;

	const unsigned char* bc;
	unsigned int len = 0;
	get_syn_bc<T>(bc, len);
	typename lex::Lexical<T>::LexicalPtr lex =
			lex::Lexical<T>::create_by_static_bc(bc, len);
	lex->parse(in);


	SyntaxPtr syntax(new Syntax());
	SynState state = X_BEGIN;
	SynDefine<T> define;
	typename SynDefine<T>::Array defines;
	ActionList actions;
	//-- 这个是临时用用的，用来把名字映射成ID
	std::vector<std::basic_string<T> > named_map;

	lex::Token<T> token;
	while (lex->fetch_next(token))
	{
		if (state == X_BEGIN)
		{
			if (token.action == T_LEX_DEF)
			{
				if (syntax->_lex)
					parse_error("Lexical description already exist", token);
				syntax->_lex = lex::Lexical<T>::create(
						token.str.substr(2, token.str.length() - 4));
			}
			else if (token.action == T_X_NAME)
			{
				define.name = token.str;
				define.schemas.push_back(SynSchema<T>());
				state = X_XNAME;
			}
			else
				parse_error("Unexpected token", token);
		}
		else if (state == X_XNAME)
		{
			if (token.action == T_COLON)
			{
				state = X_COLON;
			}
			else
				parse_error("Unexpected token", token);
		}
		else if (state == X_COLON)
		{
			if (token.action == T_NAME)
			{
				lex::Action act = syntax->_lex->get_named_action_id(token.str);
				if (act == (unsigned int)-1)
					parse_error("Undefined token: " + str<char>(token.str), token);
				define.schemas.back().actions.push_back(act);
				state = X_NAME;
			}
			else if (token.action == T_X_NAME)
			{
				named_map.push_back(token.str);
				define.schemas.back().actions.push_back(SYN_ACTION_BASE + named_map.size() - 1);
				state = X_NAME;
			}
			else
				parse_error("Unexpected token", token);
		}
		else if (state == X_NAME)
		{
			if (token.action == T_NAME)
			{
				lex::Action act = syntax->_lex->get_named_action_id(token.str);
				if (act == (unsigned int)-1)
					parse_error("Undefined token: " + str<char>(token.str), token);
				define.schemas.back().actions.push_back(act);
				state = X_NAME;
			}
			else if (token.action == T_X_NAME)
			{
				named_map.push_back(token.str);
				define.schemas.back().actions.push_back(SYN_ACTION_BASE + named_map.size() - 1);
				state = X_NAME;
			}
			else if (token.action == T_OR)
			{
				define.schemas.push_back(SynSchema<T>());
				state = X_OR;
			}
			else if (token.action == T_SCHEMA)
			{
				std::basic_string<T> sch = token.str.substr(1, token.str.size() - 2);
				define.schemas.back().name = sch.substr(0, sch.find((T)':'));
				std::vector<std::basic_string<T> > valued;
				split<T>(sch.substr(sch.find((T)':') + 1), separator<T>(), valued);
				for (size_t i = 0; i < valued.size(); i++)
					define.schemas.back().valued_actions.push_back(__Cvt<T, unsigned int>::from_string(valued[i]));
			}
			else if (token.action == T_SEMICOLON)
			{
				defines.push_back(define);
				state = X_BEGIN;
			}
			else
				parse_error("Unexpected token", token);
		}
		else if (state == X_OR)
		{
			if (token.action == T_NAME)
			{
				lex::Action act = syntax->_lex->get_named_action_id(token.str);
				if (act == (unsigned int)-1)
					parse_error("Undefined token: " + str<char>(token.str), token);
				define.schemas.back().actions.push_back(act);
				state = X_NAME;
			}
			else if (token.action == T_X_NAME)
			{
				named_map.push_back(token.str);
				define.schemas.back().actions.push_back(SYN_ACTION_BASE + named_map.size() - 1);
				state = X_NAME;
			}
			else
				parse_error("Unexpected token", token);
		}
		else if (state == X_SCHEMA)
		{
			if (token.action == T_OR)
			{
				define.schemas.push_back(SynSchema<T>());
				state = X_OR;
			}
			else if (token.action == T_SEMICOLON)
			{
				defines.push_back(define);
				state = X_BEGIN;
			}
			else
				parse_error<T>("Unexpected token", token);

		}
		else
		{
			parse_error<T>("Internal error: wrong state.");
		}
	}
	if (state != X_BEGIN)
		parse_error<T>("Unexpected end.");
	else if (defines.size() == 0)
		parse_error<T>("Invalid syntax description.");

	// set real action ID.
	for (size_t i = 0; i < defines.size(); i++)
	{
		typename SynSchema<T>::Array& schs = defines[i].schemas;
		for (size_t j = 0; j < schs.size(); j++)
		{
			ActionList& acts = schs[j].actions;
			for (size_t k = 0; k < acts.size(); k++)
			{
				if (acts[k] >= SYN_ACTION_BASE)
				{
					unsigned int act = acts[k];
					act -= SYN_ACTION_BASE;
					bool matched = false;
					for (size_t l = 0; l < defines.size(); l++)
					{
						if (defines[l].name.compare(named_map[act]) == 0)
						{
							acts[k] = SYN_ACTION_BASE + l;
							matched = true;
							break;
						}
					}
					if (!matched)
					{
						parse_error<T>("Not a defined syntax token: "
								+ str<char>(named_map[acts[k] - SYN_ACTION_BASE]));
					}
				}
			}
		}
	}
	// Now build the DFA data for parser.
	void* syn_data = 0;
	unsigned int syn_data_size = 0;
	build_syn_dfa<T>(defines, syn_data, syn_data_size);
	//-- 如果出错会抛出异常，否则返回的数据段(syn_data)是动态分配的内存区域，存储着语法分析表的数据。

	//-- 这里需要把词法分析器生成的数据块复制到 data 指针指向的数据段中，
	//-- 由于当前 data 指针还没有被分配，所以在这里还要进行分配操作，
	syntax->_data = malloc(sizeof(SynData) +
			((LexData*)syntax->_lex->_data)->data_size + syn_data_size);
	if (!syntax->_data)
	{
		free(syn_data);
		parse_error<T>("Out of memory.");
	}

	SynData* data = (SynData*)syntax->_data;
	data->header_size = sizeof(SynData);
	data->data_size = sizeof(SynData) +
			((LexData*)syntax->_lex->_data)->data_size + syn_data_size;
	data->lexical_offset = sizeof(SynData);
	data->lexical_size = ((LexData*)syntax->_lex->_data)->data_size;
	data->syntax_offset = data->lexical_offset + data->lexical_size;
	data->syntax_size = syn_data_size;
	memcpy(((char*)data) + data->lexical_offset, syntax->_lex->_data, data->lexical_size);
	memcpy(((char*)data) + data->syntax_offset, syn_data, data->syntax_size);
	syntax->_allocated = true;
	free(syn_data);

	return syntax;
}




template <typename T>
void Syntax<T>::parse(std::basic_istream<T>& input_stream) throw(std::runtime_error)
{

}



template class Syntax<char>;
template class Syntax<unsigned char>;
template class Syntax<wchar_t>;


}
}
